package config

import (
	"fmt"
	"go-ecommerce/internal/model"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"log"
	"os"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/spf13/viper"
)

// Conf 全局配置实例
var Conf Config

// Config 配置结构体
type Config struct {
	Server struct {
		Host string `mapstructure:"host"`
		Port string `mapstructure:"port"`
	} `mapstructure:"server"`
	Database struct {
		Username string `mapstructure:"username"`
		Password string `mapstructure:"password"`
		Host     string `mapstructure:"host"`
		Port     string `mapstructure:"port"`
		Name     string `mapstructure:"name"`
		Charset  string `mapstructure:"charset"`
	} `mapstructure:"database"`
	Redis struct {
		Host     string `mapstructure:"host"`
		Port     string `mapstructure:"port"`
		Password string `mapstructure:"password"`
		DB       int    `mapstructure:"db"`
	} `mapstructure:"redis"`
	Log struct {
		Level      string `mapstructure:"level"`
		Filename   string `mapstructure:"filename"`
		MaxSize    int    `mapstructure:"max_size"`
		MaxAge     int    `mapstructure:"max_age"`
		MaxBackups int    `mapstructure:"max_backups"`
	} `mapstructure:"log"`
	Webconf struct {
		JWTSecret string `mapstructure:"JWTSecret"`
	} `mapstructure:"Webconf"`
}

// DB 全局数据库连接
var DB *gorm.DB

// Redis 全局Redis连接
var Redis *redis.Client

// InitConfig 初始化配置
func InitConfig() error {
	// 设置配置文件路径和名称
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath("../config")

	// 读取配置文件
	if err := viper.ReadInConfig(); err != nil {
		return fmt.Errorf("读取配置文件失败: %w", err)
	}

	// 解析配置到结构体
	if err := viper.Unmarshal(&Conf); err != nil {
		return fmt.Errorf("解析配置文件失败: %w", err)
	}

	return nil
}

// InitDB 初始化数据库连接
func InitDB() error {
	// 构建DSN
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local",
		Conf.Database.Username,
		Conf.Database.Password,
		Conf.Database.Host,
		Conf.Database.Port,
		Conf.Database.Name,
		Conf.Database.Charset,
	)

	// 创建自定义日志器
	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold:             time.Second, // 慢查询阈值
			LogLevel:                  logger.Info, // 日志级别
			IgnoreRecordNotFoundError: true,        // 忽略记录未找到错误
			ParameterizedQueries:      true,        // 参数化查询
			Colorful:                  false,       // 禁用彩色打印
		},
	)

	// 连接数据库
	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: newLogger,
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true, // 使用单数表名
		},
	})

	if err != nil {
		return fmt.Errorf("连接数据库失败: %w", err)
	}

	// 自动迁移错误日志表结构（只创建不存在的表，不会删除字段）
	err = DB.AutoMigrate(
		&model.Admin{},
		&model.Role{},
		&model.Permission{},
		&model.RolePermission{},
		&model.AdminRole{},
		&model.Role{},
		&model.Menu{},
	)
	if err != nil {
		panic("⚠️ 自动迁移失败：" + err.Error())
	}

	// 测试连接
	sqlDB, err := DB.DB()
	if err != nil {
		return fmt.Errorf("获取数据库连接失败: %w", err)
	}

	// 设置连接池参数
	sqlDB.SetMaxIdleConns(10)                  // 最大空闲连接数
	sqlDB.SetMaxOpenConns(100)                 // 最大打开连接数
	sqlDB.SetConnMaxLifetime(10 * time.Minute) // 连接最大生命周期

	// 测试连接是否有效
	if err := sqlDB.Ping(); err != nil {
		return fmt.Errorf("数据库ping测试失败: %w", err)
	}

	return nil
}

// InitRedis 初始化Redis连接
func InitRedis() error {
	Redis = redis.NewClient(&redis.Options{
		Addr:     Conf.Redis.Host + ":" + Conf.Redis.Port,
		Password: Conf.Redis.Password,
		DB:       Conf.Redis.DB,
	})

	// 测试连接
	_, err := Redis.Ping(Redis.Context()).Result()
	if err != nil {
		return fmt.Errorf("连接Redis失败: %w", err)
	}

	return nil
}
